package com.thank.concurrency.samples.atomic;

import com.thank.concurrency.annoations.ThreadSafe;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * @author thank
 * 演示:
 *  - 使用原子类AtomicInteger: CAS造成的ABA问题
 *  - 及该问题的处理手段AtomicStampedReference
 */
@Slf4j
@ThreadSafe
public class ABAExample {

    private static AtomicInteger atomicInteger = new AtomicInteger(10);

    private static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(10, 1);

    public static void main(String[] args) {

        /*
         *   AtomicInteger
         */

        // Thread1 (100 -> 200 -> 100)
        Thread aiThread1 = new Thread(() -> {
            log.info("-----------AtomicInteger----------");
            atomicInteger.compareAndSet(10, 20);
            atomicInteger.compareAndSet(20, 10);
        });


        // Thread2 (100 -> 200)
        Thread aiThread2 = new Thread(() -> {
           sleep(1);

            log.info("更新成功? {}", atomicInteger.compareAndSet(10, 30));
            log.info("atomicInteger value: {}", atomicInteger.get());
        });


        aiThread1.start();
        aiThread2.start();


        /*
         * AtomicStampedReference
         */

        Thread asrThread1 = new Thread(() -> {
            sleep(2);
            log.info("----------AtomicStampedReference-----------");
            atomicStampedReference.compareAndSet(10, 20, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
            atomicStampedReference.compareAndSet(20, 10, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
        });

        Thread asrThread2 = new Thread(() -> {
            int stamp = atomicStampedReference.getStamp();
            log.info("before asrThread1 update: stamp: {} ", stamp);
            // 这里模拟CPU突然跑到asrThread1去了
            sleep(3);
            log.info("stamp: {}, after 3s stamp: {}", stamp, atomicStampedReference.getStamp());
            log.info("更新成功? {}", atomicStampedReference.compareAndSet(10, 30, stamp, stamp + 1));
            log.info("atomicStampedReference value: {}", atomicStampedReference.getReference());
        });

        asrThread1.start();
        asrThread2.start();
    }

    private static void sleep(int second) {
        try {
            TimeUnit.SECONDS.sleep(second);
        } catch (InterruptedException e) {
            log.error("thread3 sleep error: {}", e);
        }
    }
}
